/**
 * 代码高亮和Markdown渲染工具
 * 支持多种编程语言的语法高亮和Markdown渲染
 */
import highlightjs from "highlight.js";
import DOMPurify from "dompurify";
import { marked } from "marked";

// highlight.js完整包已经内置了所有语言支持，无需手动注册

// 语言别名映射表
const languageAliases = {
  js: "javascript",
  ts: "typescript",
  py: "python",
  rb: "ruby",
  sh: "bash",
  zsh: "bash",
  md: "markdown",
  html: "xml",
  htm: "xml",
  jsx: "javascript",
  tsx: "typescript",
  yml: "yaml",
  golang: "go",
  "objective-c": "objectivec",
  objc: "objectivec",
  cs: "csharp",
  "c++": "cpp",
  h: "c",
  cc: "cpp",
  // 添加更多语言别名
  txt: "plaintext",
  json: "json",
  xml: "xml",
  css: "css",
  sass: "scss",
  less: "less",
  dart: "dart",
  rust: "rust",
  kotlin: "kotlin",
  swift: "swift",
  php: "php",
  ini: "ini",
  toml: "toml",
  dockerfile: "dockerfile",
  makefile: "makefile",
  powershell: "powershell",
  ps1: "powershell",
  rs: "rust",
};

// 配置marked选项
marked.setOptions({
  renderer: new marked.Renderer(),
  highlight: function (code, lang) {
    const language = languageAliases[lang] || lang || "plaintext";
    try {
      if (highlightjs.getLanguage(language)) {
        return highlightjs.highlight(code, { language }).value;
      }
      return highlightjs.highlightAuto(code).value;
    } catch (e) {
      console.error("代码高亮出错:", e);
      return code;
    }
  },
  pedantic: false,
  gfm: true,
  breaks: true,
  sanitize: false,
  smartypants: false,
  xhtml: false,
});

// 自定义的代码块正则表达式
const codeBlockRegexes = [
  // Markdown风格的代码块，支持语言标记
  /```([a-z0-9]*)\s*\n([\s\S]*?)```/gi,

  // 单行代码块，使用单个反引号
  /`([^`\n]+)`/g,

  // 代码围栏式，使用~~~
  /~~~([a-z0-9]*)\s*\n([\s\S]*?)~~~/gi,

  // HTML风格的代码块
  /<pre><code(?:\s+class="language-([a-z0-9]+)")?>([^<]+)<\/code><\/pre>/gi,

  // 缩进式代码块 (4个空格或1个制表符)
  /((?:^(?:\t|    ).+\n)+)/gm,
];

// 链接正则表达式
const urlRegex =
  /(\b(?:https?:\/\/|www\.)[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*))/gi;

// Markdown 特征检测正则
const markdownFeatureRegexes = [
  // 标题
  /^#{1,6}\s+.+$/m,
  // 列表项
  /^[-*+]\s+.+$/m,
  // 有序列表
  /^\d+\.\s+.+$/m,
  // 引用块
  /^>\s+.+$/m,
  // 代码块
  /^```[\w]*\s*$/m,
  // 水平线
  /^(?:-{3,}|\*{3,}|_{3,})$/m,
  // 链接
  /\[.+?\]\(.+?\)/,
  // 图片
  /!\[.+?\]\(.+?\)/,
  // 加粗
  /\*\*.+?\*\*|__.+?__/,
  // 斜体
  /\*.+?\*|_.+?_/,
  // 任务列表
  /^\s*- \[ \]|\s*- \[x\]/m,
];

/**
 * 处理内容渲染，包括代码高亮和Markdown渲染
 * @param {string} content 要渲染的内容
 * @param {Object} options 配置选项
 * @returns {string} 处理后的HTML内容
 */
export function renderContent(content, options = {}) {
  if (!content) return "";

  // 默认配置
  const defaultOptions = {
    markdown: true, // 是否启用markdown渲染
    codeHighlight: true, // 是否启用代码高亮
    linkNewWindow: true, // 链接是否在新窗口打开
    lazyLoadImages: true, // 是否懒加载图片
    purifyHtml: true, // 是否净化HTML防止XSS
    headingAnchors: true, // 是否为标题添加锚点
    lineNumbers: false, // 是否显示代码行号
    autoDetectLinks: true, // 是否自动检测文本中的链接
    forceMarkdown: false, // 是否强制使用Markdown渲染
  };

  const opts = { ...defaultOptions, ...options };

  try {
    let processedContent = content;

    // 增强的Markdown检测逻辑
    const hasMarkdownFeatures = markdownFeatureRegexes.some((regex) =>
      regex.test(content)
    );
    const containsHtmlTags =
      /<\/?(?:div|p|span|h[1-6]|ul|ol|li|table|tr|td|img|a|br|hr|strong|em|code|pre)[^>]*>/i.test(
        content
      );

    // 以下情况认为是Markdown内容：
    // 1. 不包含HTML标签但包含Markdown特征
    // 2. 强制使用Markdown渲染选项
    const isMarkdownContent =
      (hasMarkdownFeatures && !containsHtmlTags) || opts.forceMarkdown;

    // 保存代码块，避免被marked处理
    const codeBlocks = [];

    // 如果检测到是Markdown内容且启用了Markdown渲染
    if (isMarkdownContent && opts.markdown) {
      // 先保存代码块
      processedContent = processedContent.replace(
        /```([a-z0-9]*)\s*\n([\s\S]*?)```/gi,
        (match, lang, code) => {
          const placeholder = `CODE_BLOCK_PLACEHOLDER_${codeBlocks.length}`;
          codeBlocks.push({ lang, code, original: match });
          return placeholder;
        }
      );

      // 解析Markdown
      processedContent = marked.parse(processedContent);

      // 恢复代码块并应用高亮
      codeBlocks.forEach((block, index) => {
        const placeholder = `CODE_BLOCK_PLACEHOLDER_${index}`;
        let language = languageAliases[block.lang] || block.lang || "plaintext";
        let highlightedCode;

        try {
          // 检查语言是否有效
          if (!highlightjs.getLanguage(language)) {
            console.warn(`不支持的语言: ${language}，使用纯文本`);
            highlightedCode = block.code;
            language = "plaintext";
          } else {
            highlightedCode = highlightjs.highlight(block.code, {
              language,
            }).value;
          }
        } catch (err) {
          console.error("代码高亮处理失败:", err);
          highlightedCode = block.code;
          language = "plaintext";
        }

        const replacement = `<pre data-language="${language}"><code class="language-${language}">${highlightedCode}</code></pre>`;
        processedContent = processedContent.replace(placeholder, replacement);
      });
    } else if (opts.codeHighlight) {
      // 处理富文本中的代码块

      // 1. 处理已经使用<pre><code>标签包裹的代码块
      processedContent = processedContent.replace(
        /<pre><code(?:\s+class="language-([a-z0-9]+)")?>([^<]+)<\/code><\/pre>/gi,
        (match, lang, code) => {
          try {
            const decodedCode = code
              .replace(/&lt;/g, "<")
              .replace(/&gt;/g, ">")
              .replace(/&amp;/g, "&");
            const language = languageAliases[lang] || lang || "plaintext";
            const highlighted = highlightjs.highlight(decodedCode, {
              language,
            }).value;
            return `<pre data-language="${language}"><code class="language-${language}">${highlighted}</code></pre>`;
          } catch (err) {
            console.error("代码高亮处理失败:", err);
            return match;
          }
        }
      );

      // 2. 处理使用```风格的代码块
      processedContent = processedContent.replace(
        /```([a-z0-9]*)\s*\n([\s\S]*?)```/gi,
        (match, lang, code) => {
          try {
            // 移除可能的HTML编码
            const decodedCode = code
              .replace(/&lt;/g, "<")
              .replace(/&gt;/g, ">")
              .replace(/&amp;/g, "&");
            const language = languageAliases[lang] || lang || "plaintext";
            const highlighted = highlightjs.highlight(decodedCode, {
              language,
            }).value;
            return `<pre data-language="${language}"><code class="language-${language}">${highlighted}</code></pre>`;
          } catch (err) {
            console.error("代码高亮处理失败:", err);
            return match;
          }
        }
      );

      // 3. 处理使用~~~风格的代码块
      processedContent = processedContent.replace(
        /~~~([a-z0-9]*)\s*\n([\s\S]*?)~~~/gi,
        (match, lang, code) => {
          try {
            const decodedCode = code
              .replace(/&lt;/g, "<")
              .replace(/&gt;/g, ">")
              .replace(/&amp;/g, "&");
            const language = languageAliases[lang] || lang || "plaintext";
            const highlighted = highlightjs.highlight(decodedCode, {
              language,
            }).value;
            return `<pre data-language="${language}"><code class="language-${language}">${highlighted}</code></pre>`;
          } catch (err) {
            console.error("代码高亮处理失败:", err);
            return match;
          }
        }
      );

      // 4. 处理行内代码块（单反引号）
      processedContent = processedContent.replace(
        /`([^`]+)`/g,
        (match, code) => {
          return `<code class="inline-code">${code}</code>`;
        }
      );
    }

    // 自动检测纯文本URL并转换为链接
    if (opts.autoDetectLinks && !isMarkdownContent) {
      // 避免处理已经在<a>标签内的内容
      const tempDivEl = document.createElement("div");
      tempDivEl.innerHTML = processedContent;

      // 遍历所有文本节点
      const walk = document.createTreeWalker(
        tempDivEl,
        NodeFilter.SHOW_TEXT,
        null,
        false
      );

      const nodesToReplace = [];
      let currentNode;

      while ((currentNode = walk.nextNode())) {
        // 跳过已在<a>、<pre>、<code>标签内的文本节点
        let parent = currentNode.parentNode;
        let skip = false;

        while (parent) {
          if (
            parent.nodeName === "A" ||
            parent.nodeName === "PRE" ||
            parent.nodeName === "CODE"
          ) {
            skip = true;
            break;
          }
          parent = parent.parentNode;
        }

        if (!skip) {
          const matches = currentNode.nodeValue.match(urlRegex);
          if (matches) {
            nodesToReplace.push({ node: currentNode, matches });
          }
        }
      }

      // 处理找到的URL
      for (const item of nodesToReplace) {
        const { node, matches } = item;
        let nodeContent = node.nodeValue;

        for (const url of matches) {
          const href = url.startsWith("www.") ? `https://${url}` : url;
          const linkHtml = `<a href="${href}" target="_blank" rel="noopener noreferrer">${url}</a>`;
          nodeContent = nodeContent.replace(url, linkHtml);
        }

        const tempDiv = document.createElement("div");
        tempDiv.innerHTML = nodeContent;

        const fragment = document.createDocumentFragment();
        while (tempDiv.firstChild) {
          fragment.appendChild(tempDiv.firstChild);
        }

        node.parentNode.replaceChild(fragment, node);
      }

      processedContent = tempDivEl.innerHTML;
    }

    // 净化HTML
    if (opts.purifyHtml) {
      const purifyOptions = {
        ADD_TAGS: ["iframe", "video", "audio", "pre", "code"],
        ADD_ATTR: [
          "target",
          "frameborder",
          "allowfullscreen",
          "controls",
          "loading",
          "class",
          "data-language",
          "rel",
        ],
        USE_PROFILES: { html: true },
      };
      processedContent = DOMPurify.sanitize(processedContent, purifyOptions);
    }

    return processedContent;
  } catch (error) {
    console.error("渲染内容出错:", error);
    return content; // 如果出错，返回原始内容
  }
}

/**
 * 客户端应用代码高亮和其他增强效果
 * 在内容渲染到DOM后调用
 * @param {string} selector 要处理的元素选择器
 */
export function applyHighlighting(selector = ".moment-content") {
  // 等待DOM更新完成
  setTimeout(() => {
    try {
      // 查找所有代码块并应用高亮
      const codeBlocks = document.querySelectorAll(`${selector} pre code`);
      if (codeBlocks.length > 0) {
        codeBlocks.forEach((block) => {
          try {
            // 检查是否已经高亮过
            if (block.classList.contains("hljs")) return;

            // 尝试确定语言
            const classNames = Array.from(block.classList || []);
            const langClass = classNames.find((className) =>
              className.startsWith("language-")
            );
            let language = langClass ? langClass.replace("language-", "") : "";

            // 应用高亮 - 增加错误处理
            try {
              // 首先检查语言是否存在和有效
              if (!highlightjs.getLanguage(language)) {
                block.className = "language-plaintext hljs";
                // 纯文本不需要高亮
                block.textContent = block.textContent;
              } else {
                // 正常高亮
                highlightjs.highlightElement(block);
              }
            } catch (err) {
              console.error("代码高亮失败，使用纯文本显示:", err);
              // 失败时也使用纯文本
              block.className = "language-plaintext hljs";
            }

            // 获取语言类型并设置到父元素
            const parent = block.parentElement;
            if (parent && !parent.hasAttribute("data-language")) {
              const detectedLang = langClass
                ? langClass.replace("language-", "")
                : "plaintext";
              parent.setAttribute("data-language", detectedLang);
            }

            // 添加行号
            if (
              parent &&
              parent.classList.contains("with-line-numbers") &&
              !parent.querySelector(".line-numbers-container")
            ) {
              addLineNumbers(parent, block);
            }

            // 为代码块添加复制按钮
            if (parent && !parent.querySelector(".code-copy-btn")) {
              addCopyButton(parent);
            }
          } catch (err) {
            console.error("代码块高亮处理失败:", err);
          }
        });
      }

      // 处理代码块外观见的代码文本 - 尝试找出可能是代码块的文本并转换
      processPlainTextCodeBlocks(selector);

      // 处理链接，使其在新窗口打开
      document.querySelectorAll(`${selector} a`).forEach((link) => {
        if (
          !link.getAttribute("target") &&
          !link.getAttribute("href")?.startsWith("#")
        ) {
          link.setAttribute("target", "_blank");
          link.setAttribute("rel", "noopener noreferrer");
        }
      });

      // 处理图片懒加载
      document.querySelectorAll(`${selector} img`).forEach((img) => {
        if (!img.hasAttribute("loading")) {
          img.setAttribute("loading", "lazy");
        }

        // 添加错误处理
        img.addEventListener("error", function () {
          if (!this.classList.contains("lazy-load-error")) {
            this.classList.add("lazy-load-error");
            this.src =
              "";
          }
        });
      });
    } catch (error) {
      console.error("应用高亮和增强效果失败:", error);
    }
  }, 0);
}

/**
 * 处理纯文本中可能是代码块的部分
 * @param {string} selector 要处理的元素选择器
 */
function processPlainTextCodeBlocks(selector) {
  const contentElements = document.querySelectorAll(selector);

  contentElements.forEach((element) => {
    // 遍历子节点，查找可能包含代码块的文本节点
    // 先处理明显的代码块模式

    // 1. 查找可能是Markdown风格代码块的文本
    // 使用正则表达式匹配```代码块```模式
    let html = element.innerHTML;
    let hasChanges = false;

    // 确保不处理已经在pre或code标签中的内容
    // 通过临时替换标签内容来实现
    const preCodeBlocks = [];
    html = html.replace(/<pre[\s\S]*?<\/pre>/gi, (match) => {
      preCodeBlocks.push(match);
      return `%%PRE_CODE_BLOCK_${preCodeBlocks.length - 1}%%`;
    });

    // 处理```代码块```模式
    html = html.replace(
      /```([a-z0-9]*)\s*\n([\s\S]*?)```/gi,
      (match, lang, code) => {
        hasChanges = true;
        const language = languageAliases[lang] || lang || "plaintext";
        try {
          // 检查语言是否有效
          if (!highlightjs.getLanguage(language)) {
            // 使用纯文本处理
            return `<pre data-language="plaintext"><code class="language-plaintext">${code}</code></pre>`;
          }
          // 正常高亮
          const highlighted = highlightjs.highlight(code, { language }).value;
          return `<pre data-language="${language}"><code class="language-${language}">${highlighted}</code></pre>`;
        } catch (e) {
          console.error("高亮处理失败，回退到纯文本:", e);
          return `<pre data-language="plaintext"><code class="language-plaintext">${code}</code></pre>`;
        }
      }
    );

    // 处理~~~代码块~~~模式
    html = html.replace(
      /~~~([a-z0-9]*)\s*\n([\s\S]*?)~~~/gi,
      (match, lang, code) => {
        hasChanges = true;
        const language = languageAliases[lang] || lang || "plaintext";
        try {
          // 检查语言是否有效
          if (!highlightjs.getLanguage(language)) {
            // 使用纯文本处理
            return `<pre data-language="plaintext"><code class="language-plaintext">${code}</code></pre>`;
          }
          // 正常高亮
          const highlighted = highlightjs.highlight(code, { language }).value;
          return `<pre data-language="${language}"><code class="language-${language}">${highlighted}</code></pre>`;
        } catch (e) {
          console.error("高亮处理失败，回退到纯文本:", e);
          return `<pre data-language="plaintext"><code class="language-plaintext">${code}</code></pre>`;
        }
      }
    );

    // 恢复pre标签内容
    preCodeBlocks.forEach((block, index) => {
      html = html.replace(`%%PRE_CODE_BLOCK_${index}%%`, block);
    });

    if (hasChanges) {
      element.innerHTML = html;

      // 为新添加的代码块添加复制按钮
      element
        .querySelectorAll("pre:not(:has(.code-copy-btn))")
        .forEach((pre) => {
          addCopyButton(pre);
        });
    }
  });
}

/**
 * 为代码块添加行号
 * @param {Element} preElement pre元素
 * @param {Element} codeElement code元素
 */
function addLineNumbers(preElement, codeElement) {
  const codeLines = codeElement.textContent.split("\n").length;
  const lineNumbersContainer = document.createElement("div");
  lineNumbersContainer.className = "line-numbers-container";

  for (let i = 1; i <= codeLines; i++) {
    const lineSpan = document.createElement("span");
    lineSpan.className = "line-number";
    lineSpan.textContent = i;
    lineNumbersContainer.appendChild(lineSpan);
  }

  preElement.insertBefore(lineNumbersContainer, codeElement);
  preElement.classList.add("with-line-numbers");
}

/**
 * 为代码块添加复制按钮
 * @param {Element} preElement pre元素
 */
function addCopyButton(preElement) {
  const copyBtn = document.createElement("button");
  copyBtn.className = "code-copy-btn";
  copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
  copyBtn.title = "复制代码";

  copyBtn.addEventListener("click", () => {
    const code = preElement.querySelector("code");
    if (code) {
      try {
        // 获取纯文本内容
        const text = code.textContent || "";

        // 复制到剪贴板
        navigator.clipboard
          .writeText(text)
          .then(() => {
            copyBtn.innerHTML = '<i class="fas fa-check"></i>';
            copyBtn.classList.add("success");

            // 2秒后恢复原样
            setTimeout(() => {
              copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
              copyBtn.classList.remove("success");
            }, 2000);
          })
          .catch((err) => {
            console.error("复制失败:", err);
            copyBtn.innerHTML = '<i class="fas fa-times"></i>';
            copyBtn.classList.add("error");

            // 2秒后恢复原样
            setTimeout(() => {
              copyBtn.innerHTML = '<i class="fas fa-copy"></i>';
              copyBtn.classList.remove("error");
            }, 2000);
          });
      } catch (err) {
        console.error("复制操作失败:", err);
      }
    }
  });

  preElement.appendChild(copyBtn);
}

/**
 * 检测给定文本是否可能是代码块
 * @param {string} text 要检查的文本
 * @returns {boolean} 是否是代码块
 */
function isPossibleCodeBlock(text) {
  // 移除前后空白
  const trimmed = text.trim();

  // 检查常见的代码特征
  const codePatterns = [
    /^import\s+[\w\s,{}]+\s+from\s+['"][^'"]+['"];?$/m, // JS/TS import
    /^(const|let|var)\s+\w+\s*=\s*.+;?$/m, // JS/TS 变量声明
    /^function\s+\w+\s*\([^)]*\)\s*{/m, // JS function声明
    /^class\s+\w+(\s+extends\s+\w+)?\s*{/m, // JS/TS/Java类声明
    /^\s*public\s+(static\s+)?(void|class|interface|enum)\s+\w+/m, // Java方法/类
    /^def\s+\w+\s*\([^)]*\):/m, // Python函数定义
    /^#include\s+[<"][^>"]+[>"]$/m, // C/C++ include
    /^package\s+[\w.]+;?$/m, // Java/Kotlin package
    /^using\s+[\w.]+;?$/m, // C# using
    /^\s*@[\w.]+(\([^)]*\))?$/m, // Java/TS装饰器
    /^\s*<\?php/m, // PHP开头
  ];

  // 如果匹配任何代码模式，认为是代码块
  for (const pattern of codePatterns) {
    if (pattern.test(trimmed)) {
      return true;
    }
  }

  // 检查缩进一致的多行
  const lines = trimmed.split("\n");
  if (lines.length >= 3) {
    // 统计每行开头空格数
    const indentCounts = lines.map((line) => {
      const match = /^(\s*)/.exec(line);
      return match[1].length;
    });

    // 检查是否有统一的缩进模式
    const indentSizes = new Set(indentCounts.filter((c) => c > 0));
    if (indentSizes.size === 1 && Array.from(indentSizes)[0] >= 2) {
      // 行首一致缩进，可能是代码
      return true;
    }
  }

  return false;
}

// 暴露的方法
export default {
  renderContent,
  applyHighlighting,
  highlightjs,
};
